/// <reference path="Addition.js" />
Type.registerNamespace("BM");

// Constraint object

BM.Constraint = function(item, additionNode) {
    ///	<summary>
    ///	Constraint is a special type of Addition, it is pinned in items' validation process.
    /// In order for the item to be valid, all its constraints must be met.
    ///	</summary>
    ///	<returns type="BM.Constraint" />
    BM.Constraint.initializeBase(this, [item, additionNode]);
}

BM.Constraint.prototype = {
    load: function() {
        BM.Constraint.callBaseMethod(this, 'load');
        this.isMet();
    },

    getErrorText: function() {
        /// <returns type="String" mayBeNull="true" >
        /// gets the attached error text
        /// </returns>
        if (this.errorText) {
            return this.errorText;
        }
        if (this.additionNode && this.additionNode.getAttribute('errorText')) {
            this.errorText = this.additionNode.getAttribute('errorText');
            return this.errorText;
        }
        return this.getDefaultErrorText();
    },

    getDefaultErrorText: function() {
        /// <returns type="String" mayBeNull="true" >
        /// gets the error text that should be displayed when there is no attached error text
        /// </returns>
        return null;
    },

    setErrorText: function(value) {
        /// <summary>
        /// sets the text to be shown on error
        /// </summary>
        /// <param name="value" type="String" mayBeNull="true">
        /// Event handler
        /// </param>
        this.errorText = value;
    },

    addMet: function(handler) {
        /// <summary>
        /// Add an event handler for the Met event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().addHandler('met', handler);
    },

    removeMet: function(handler) {
        /// <summary>
        /// Remove an event handler from the Met event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().removeHandler('met', handler);
    },

    raiseMet: function(eventArgs) {
        /// <summary>
        /// Raise the Met event
        /// </summary>
        /// <param name="eventArgs" type="Sys.EventArgs" mayBeNull="false">
        /// Event arguments for the Met event
        /// </param>
        /// <returns />

        var handler = this.get_events().getHandler('met');
        if (handler) {
            handler(this, eventArgs);
        }
    },

    addNotMet: function(handler) {
        /// <summary>
        /// Add an event handler for the NotMet event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().addHandler('notMet', handler);
    },

    removeNotMet: function(handler) {
        /// <summary>
        /// Remove an event handler from the NotMet event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().removeHandler('notMet', handler);
    },

    raiseNotMet: function(eventArgs) {
        /// <summary>
        /// Raise the NotMet event
        /// </summary>
        /// <param name="eventArgs" type="Sys.EventArgs" mayBeNull="false">
        /// Event arguments for the NotMet event
        /// </param>
        /// <returns />

        var handler = this.get_events().getHandler('notMet');
        if (handler) {
            handler(this, eventArgs);
        }
    },

    check: function() {
        ///	<summary>
        ///		returns information if the validation on the constraint was successful        
        ///		and performs attached events
        ///	</summary>
        ///	<returns type="Boolean" />
        var isMet = this.isMet();
        if (isMet) {
            this.raiseMet()
        } else {
            this.raiseNotMet();
        }
        return isMet;
    },

    isMet: function() {
        ///	<summary>
        ///		returns information if the validation on the constraint was successful                
        ///	</summary>
        ///	<returns type="Boolean" />        
        return true;
    }
}

BM.Constraint.registerClass("BM.Constraint", BM.Addition);

// Data type constraint object

BM.DataTypeConstraint = function(leafItem) {
    ///	<summary>
    ///		This class checks if the data type of the item matches its value.
    /// inherits from BM.Constraint
    ///	</summary>
    BM.DataTypeConstraint.initializeBase(this, [leafItem]);
}

BM.DataTypeConstraint.prototype = {
    getDataType: function() {
        ///	<summary>
        ///		returns item data type
        ///	</summary>
        ///	<returns type="String" />        
        return this.item.dataType;
    },

    isMet: function() {
        var dataTypeValue;
        try {
            dataTypeValue = this.getItem().getDataTypeValue();
        } catch (e) {
        }

        return BM.DataTypeConstraint.isValid(this.getDataType(), dataTypeValue);
    },

    getDefaultErrorText: function() {
        var defaultErrorText = BM.Messages['DataTypeConstraintError'] + ": " + BM.DataTypes[this.getDataType()];
        if (BM.DataTypeConstraint.isDateDataType(this.getDataType())) {
            defaultErrorText += ". " + BM.Messages['ExampleDateTime'] + (new Date()).localeFormat(this.getItem().getMForm().get_culturalContext().getFormat(this.getDataType()));
        }
        return defaultErrorText;
    },

    appendDescription: function(node) {
        var ct = BM.NodeBuilder.createDiv(node, "Constraint");
        BM.NodeBuilder.createSpan(ct, 'Type', BM.DataTypes['dataType'] + ':');
        BM.NodeBuilder.createSpan(ct, 'Value', BM.DataTypes[this.getDataType()]);
    }
}

BM.Addition['DataTypeConstraint'] = BM.DataTypeConstraint;
BM.DataTypeConstraint.registerClass("BM.DataTypeConstraint", BM.Constraint);

BM.DataTypeConstraint.isValid = function(dataType, fieldValue) {
    if (dataType == "NonNegativeInteger" && !(fieldValue >= 0)) {
        return false;
    }
    if (dataType == "PositiveInteger" && !(fieldValue > 0)) {
        return false;
    }
    if (dataType == "NegativeInteger" && !(fieldValue < 0)) {
        return false;
    }
    if (dataType == "NonPositiveInteger" && !(fieldValue <= 0)) {
        return false;
    }
    if (BM.DataTypeConstraint.isDateDataType(dataType)) {
        return fieldValue != null;
    }
    switch (dataType) {
        case "Int":
        case "Integer":
        case "NonNegativeInteger":
        case "NonPositiveInteger":
        case "PositiveInteger":
        case "NegativeInteger":
            return BM.DataTypeConstraint.isInt(fieldValue);
        case "Float":
        case "Decimal":
        case "Double":
            return BM.DataTypeConstraint.isFloat(fieldValue);
        case "String":
            return BM.DataTypeConstraint.isNotEmpty(fieldValue);
        case "Boolean":
            return BM.DataTypeConstraint.isBoolean(fieldValue);
    }
    return BM.DataTypeConstraint.isNotEmpty(fieldValue);
}

BM.DataTypeConstraint.isNotEmpty = function(fieldValue) {
    return (fieldValue != "");
}

BM.DataTypeConstraint.isInt = function(value) {
    return parseInt(value) == value;
}

BM.DataTypeConstraint.isFloat = function(value) {
    return parseFloat(value) == value;
}

BM.DataTypeConstraint.isBoolean = function(fieldValue) {
    if (fieldValue == 'false') return true;
    if (fieldValue == 'true') return true;
    return false;
}

BM.DataTypeConstraint.isDateDataType = function(dataType) {
    if (dataType == 'Date') return true;
    if (dataType == 'DateTime') return true;
    if (dataType == 'GYear') return true;
    if (dataType == 'GYearMonth') return true;
    if (dataType == 'GMonth') return true;
    if (dataType == 'GDay') return true;
    if (dataType == 'GMonthDay') return true;
    if (dataType == 'Time') return true;
    return false;
}

BM.DataTypeConstraint.isNumberDataType = function(dataType) {
    switch (dataType) {
        case "Int":
        case "Integer":
        case "NonNegativeInteger":
        case "NonPositiveInteger":
        case "PositiveInteger":
        case "NegativeInteger":
        case "Float":
        case "Decimal":
        case "Double":
            return true;
    }
    return false;
}
